package cn.psvmc.cxzapi.service.eqb;

import cn.psvmc.cxzapi.config.ConfigKey;
import cn.psvmc.cxzapi.exception.ServiceException;
import cn.psvmc.cxzapi.mapper.customer.TCustomerMapper;
import cn.psvmc.cxzapi.mapper.order.TOrderContractMapper;
import cn.psvmc.cxzapi.mapper.store.TStoreDetailMapper;
import cn.psvmc.cxzapi.model.eqb.Component;
import cn.psvmc.cxzapi.model.eqb.TemplateDTO;
import cn.psvmc.cxzapi.model.order.TOrder;
import cn.psvmc.cxzapi.model.order.TOrderBill;
import cn.psvmc.cxzapi.model.order.TOrderContract;
import cn.psvmc.cxzapi.util.*;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.FileEntity;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.util.*;

/**
 * @author ywx
 * @className EqbService
 * @description
 * @date 2024/10/2 22:50
 **/
@Service
@Slf4j
public class EqbService {
    @Resource
    private TCustomerMapper tCustomerMapper;
    @Resource
    private TStoreDetailMapper tStoreDetailMapper;
    @Resource
    private TOrderContractMapper orderContractMapper;

    /**
     * @Description 计算字符串的Content-MD5
     * @Date 2024/10/11 17:10
     * @Author YWX
     * @Param [str]
     * @Return java.lang.String
     **/
    public static String getStringContentMD5(String str) {
        // 获取文件MD5的二进制数组（128位）
        byte[] bytes = EqbService.getFileMD5Bytes1282(str);
        // 对文件MD5的二进制数组进行base64编码
        return new String(Base64.encodeBase64(bytes));
    }

    /**
     * @Description 获取文件MD5-二进制数组（128位）
     * @Date 2024/10/11 17:10
     * @Author YWX
     * @Param [filePath]
     * @Return byte[]
     **/
    public static byte[] getFileMD5Bytes1282(String filePath) {
        FileInputStream fis = null;
        byte[] md5Bytes = null;
        try {
            File file = new File(filePath);
            fis = new FileInputStream(file);
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[1024];
            int length = -1;
            while ((length = fis.read(buffer, 0, 1024)) != -1) {
                md5.update(buffer, 0, length);
            }
            md5Bytes = md5.digest();
            fis.close();
        } catch (FileNotFoundException e) {
            EqbService.log.error("文件不存在：{}", filePath);
        } catch (NoSuchAlgorithmException e) {
            EqbService.log.error("没有MD5算法！");
        } catch (IOException e) {
            EqbService.log.error("文件读取失败：{}", e.getMessage());
        }
        return md5Bytes;
    }

    public String HMACSHA256(String data, String key) {
        try {
            Mac mac = Mac.getInstance("HmacSHA256");
            mac.init(new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
            byte[] signData = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
            StringBuilder sb = new StringBuilder();
            for (byte item : signData) {
                sb.append(Integer.toHexString((item & 0xFF) | 0x100), 1, 3);
            }
            return sb.toString();
        } catch (NoSuchAlgorithmException | InvalidKeyException | IllegalStateException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @Description 查询模板文件详情
     * @Date 2024/10/3 20:11
     * @Author YWX
     * @Param [templateId]
     * @Return java.lang.String
     **/
    public String getDocTemplate(String templateId) {
        //return testGet("/v1/docTemplates/" + templateId);
        return EqbService.testGet("/v3/doc-templates/" + templateId);
    }

    /**
     * @Description 获取模板文件上传地址
     * @Date 2024/10/3 20:11
     * @Author YWX
     * @Param [object]
     * @Return java.lang.String
     **/
    public String createByUploadUrl(JSONObject object) {
        /*JSONObject object = new JSONObject();
        object.put("contentMd5", dto.getContentMd5());
        object.put("contentType", dto.getContentType());
        object.put("fileName", dto.getFileName());
        object.put("convert2Pdf", dto.getConvert2Pdf());*/
        //return testPost(object, "/v1/docTemplates/createByUploadUrl");
        return EqbService.testPost(object, "/v3/files/file-upload-url");
    }

    /**
     * @Description 请求签名鉴权-POST请求
     * @Date 2024/10/3 19:54
     * @Author YWX
     * @Param [reqBodyObj, api]
     * @Return java.lang.String
     **/
    public static String testPost(JSONObject reqBodyObj, String api) {
        // 接口请求地址
        String accountsApiUrl = ConfigKey.eHostUrl + api;

        try {
            // 请求Body体数据
            String reqBodyData = reqBodyObj.toString();
            // 对请求Body体内的数据计算ContentMD5
            String contentMD5 = EqbService.doContentMD5(reqBodyData);

            // 构建待签名字符串
            String method = "POST";
            String accept = "*/*";
            String contentType = "application/json; charset=UTF-8";
            String date = "";
            String headers = "";

            StringBuilder sb = new StringBuilder();
            sb.append(method).append("\n").append(accept).append("\n").append(contentMD5).append("\n")
                    .append(contentType).append("\n").append(date).append("\n");
            if (headers.isEmpty()) {
                sb.append(headers).append(api);
            } else {
                sb.append(headers).append("\n").append(api);
            }

            // 构建参与请求签名计算的明文
            String plaintext = sb.toString();
            // 计算请求签名值
            String reqSignature = EqbService.doSignatureBase64(plaintext, ConfigKey.eAppSecret);

            // 获取时间戳(精确到毫秒)
            long timeStamp = EqbService.timeStamp();

            // 构建请求头
            LinkedHashMap<String, String> header = new LinkedHashMap<>();
            header.put("X-Tsign-Open-App-Id", ConfigKey.eAppId);
            header.put("X-Tsign-Open-Auth-Mode", "Signature");
            header.put("X-Tsign-Open-Ca-Timestamp", String.valueOf(timeStamp));
            header.put("Accept", accept);
            header.put("Content-Type", contentType);
            header.put("X-Tsign-Open-Ca-Signature", reqSignature);
            header.put("Content-MD5", contentMD5);

            // 发送POST请求
            String result = HTTPHelper.sendPOST(accountsApiUrl, reqBodyData, header, "UTF-8");
            EqbService.log.info("post请求返回信息： {}", result);
            return result;
        } catch (Exception e) {
            String msg = MessageFormat.format("请求签名鉴权方式调用接口出现异常: {0}", e.getMessage());
            EqbService.log.error(msg);
            throw new RuntimeException(msg);
        }
    }

    /**
     * @Description 请求签名鉴权-GET请求
     * @Date 2024/10/11 17:06
     * @Author YWX
     * @Param [api]
     * @Return java.lang.String
     **/
    public static String testGet(String api) {
        // 签署流程查询接口请求地址
        String hostGetSignFlowApi = ConfigKey.eHostUrl + api;

        try {
            // GET请求时ContentMD5为""
            String contentMD5 = "";

            // 构建待签名字符串
            String method = "GET";
            String accept = "*/*";
            String contentType = "application/json; charset=UTF-8";
            String date = "";
            String headers = "";

            StringBuilder sb = new StringBuilder();
            sb.append(method).append("\n").append(accept).append("\n").append(contentMD5).append("\n")
                    .append(contentType).append("\n").append(date).append("\n");
            if (headers.isEmpty()) {
                sb.append(headers).append(api);
            } else {
                sb.append(headers).append("\n").append(api);
            }

            // 构建参与请求签名计算的明文
            String plaintext = sb.toString();
            // 计算请求签名值
            String reqSignature = EqbService.doSignatureBase64(plaintext, ConfigKey.eAppSecret);

            // 获取时间戳(精确到毫秒)
            long timeStamp = EqbService.timeStamp();

            // 构建请求头
            LinkedHashMap<String, String> header = new LinkedHashMap<>();
            header.put("X-Tsign-Open-App-Id", ConfigKey.eAppId);
            header.put("X-Tsign-Open-Auth-Mode", "Signature");
            header.put("X-Tsign-Open-Ca-Timestamp", String.valueOf(timeStamp));
            header.put("Accept", accept);
            header.put("Content-Type", contentType);
            header.put("X-Tsign-Open-Ca-Signature", reqSignature);
            header.put("Content-MD5", contentMD5);

            // 发送GET请求
            String result = HTTPHelper.sendGet(hostGetSignFlowApi, header, "UTF-8");
            EqbService.log.info("get请求返回信息： {}", result);
            return result;
        } catch (Exception e) {
            String msg = MessageFormat.format("请求签名鉴权方式调用接口出现异常: {0}", e.getMessage());
            EqbService.log.error(msg);
            throw new RuntimeException(msg);
        }
    }

    //上传文件流
    public static boolean uploadFile(String filePath, String endpointUrl) {
        boolean result = false;
        try {
            File file = new File(filePath);
            // 创建文件输入流
            FileInputStream fileInputStream = new FileInputStream(file);
            // 获取文件大小
            long fileLength = file.length();

            // 计算文件的 MD5 值
            String contentMD5 = EqbService.getStringContentMD5(filePath);

            // 创建连接
            URL url = new URL(endpointUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoOutput(true);
            connection.setRequestMethod("PUT");
            connection.setRequestProperty("Content-MD5", contentMD5);
            //connection.setRequestProperty("Content-Type", "application/octet-stream"); // 设置内容类型为二进制流
            connection.setRequestProperty("Content-Type", "application/pdf"); // 设置内容类型为二进制流
            connection.setRequestProperty("Content-Length", String.valueOf(fileLength));

            // 发送文件数据
            try (OutputStream outputStream = connection.getOutputStream()) {
                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = fileInputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
            }

            // 获取响应
            int responseCode = connection.getResponseCode();
            if (responseCode == HttpURLConnection.HTTP_OK) {
                result = true;
            } else {
                throw new ServiceException("文件流上传失败，响应码：" + responseCode);
            }

            fileInputStream.close();
            connection.disconnect();
            return result;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    //字符串转json对象
    public static JSONObject strToJsonObject(String str) {
        return JSON.parseObject(str, JSONObject.class);
    }

    /***
     *
     * @param str 待计算的消息
     * @return MD5计算后摘要值的Base64编码(ContentMD5)
     * @throws Exception 加密过程中的异常信息
     */
    public static String doContentMD5(String str) throws Exception {
        byte[] md5Bytes;
        MessageDigest md5;
        String contentMD5;
        try {
            md5 = MessageDigest.getInstance("MD5");
            // 计算md5函数
            md5.update(str.getBytes(StandardCharsets.UTF_8));
            // 获取文件MD5的二进制数组（128位）
            md5Bytes = md5.digest();
            // 把MD5摘要后的二进制数组md5Bytes使用Base64进行编码（而不是对32位的16进制字符串进行编码）
            contentMD5 = new String(Base64.encodeBase64(md5Bytes), StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException e) {
            String msg = MessageFormat.format("不支持此算法: {0}", e.getMessage());
            throw new Exception(msg, e);
        }
        return contentMD5;
    }

    /***
     * 计算请求签名值
     *
     * @param message 待计算的消息
     * @param secret 密钥
     * @return HmacSHA256计算后摘要值的Base64编码
     * @throws Exception 加密过程中的异常信息
     */
    public static String doSignatureBase64(String message, String secret) throws Exception {
        String algorithm = "HmacSHA256";
        Mac hmacSha256;
        String digestBase64;
        try {
            hmacSha256 = Mac.getInstance(algorithm);
            byte[] keyBytes = secret.getBytes(StandardCharsets.UTF_8);
            byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
            hmacSha256.init(new SecretKeySpec(keyBytes, 0, keyBytes.length, algorithm));
            // 使用HmacSHA256对二进制数据消息Bytes计算摘要
            byte[] digestBytes = hmacSha256.doFinal(messageBytes);
            // 把摘要后的结果digestBytes转换成十六进制的字符串
            // String digestBase64 = Hex.encodeHexString(digestBytes);
            // 把摘要后的结果digestBytes使用Base64进行编码
            digestBase64 = new String(Base64.encodeBase64(digestBytes), StandardCharsets.UTF_8);
        } catch (NoSuchAlgorithmException e) {
            String msg = MessageFormat.format("不支持此算法: {0}", e.getMessage());
            throw new Exception(msg, e);
        } catch (InvalidKeyException e) {
            String msg = MessageFormat.format("无效的密钥规范: {0}", e.getMessage());
            throw new Exception(msg, e);
        }
        return digestBase64;
    }

    /***
     * 获取时间戳(毫秒级)
     *
     * @return 毫秒级时间戳, 如 1578446909000
     */
    public static long timeStamp() {
        return System.currentTimeMillis();
    }

    /**
     * @Description 填充内容生成PDF
     * @Date 2024/10/3 20:29
     * @Author YWX
     * @Param [dto]
     * @Return java.lang.String
     **/
    public String createByTemplate(TemplateDTO dto) {
        JSONObject object = new JSONObject();
        /*object.put("name", dto.getName());
        object.put("templateId", dto.getTemplateId());
        object.put("simpleFormFields", dto.getSimpleFormFields());
        object.put("strictCheck", dto.getStrictCheck());
        return testPost(object, "/v1/docTemplates/createByTemplate");*/
        object.put("fileName", dto.getFileName());
        object.put("docTemplateId", dto.getDocTemplateId());
        object.put("components", dto.getComponents());
        object.put("requiredCheck", dto.getRequiredCheck());
        return EqbService.testPost(object, "/v3/files/create-by-doc-template");
    }

    public static String fileUpload(String fuploadUrl1, MultipartFile file, HttpServletRequest request) {
        try {
            CloseableHttpClient httpClient = HttpClients.createDefault();
            HttpPut httpPut = new HttpPut(fuploadUrl1);

            httpPut.setHeader("Content-MD5", request.getHeader("Content-MD5"));
            httpPut.setHeader("Content-Type", request.getHeader("Content-Type"));

            File file1 = File.createTempFile("temp", file.getOriginalFilename());
            file.transferTo(file1);
            FileEntity fileEntity = new FileEntity(file1);
            StringEntity stringEntity = new StringEntity(EntityUtils.toString(fileEntity), ContentType.APPLICATION_OCTET_STREAM);
            stringEntity.setContentType("multipart/form-data");
            httpPut.setEntity(stringEntity);
            CloseableHttpResponse response = httpClient.execute(httpPut);
            HttpEntity entity = response.getEntity();
            String responseContent = EntityUtils.toString(entity, "UTF-8");
            response.close();
            httpClient.close();
            return responseContent;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @Description 生成租赁合同-基于e签宝模板
     * @Date 2024/10/14 23:43
     * @Author YWX
     * @Param [order, bills, map]
     * @Return java.lang.String
     **/
    public TOrderContract getContractUrl(TOrder order, List<TOrderBill> bills, Map<String, String> map) {
        TOrderContract contract = new TOrderContract();
        String orderCode = order.getOrderCode();
        String username = order.getUsername();
        String userphone = order.getUserphone();
        String userAddress = order.getUserAddress();
        String legalPersonName = map.get("legalPersonName");
        String storeaddress = map.get("storeaddress");
        String storename = map.get("storename");
        String idNumber = map.get("idNumber");
        String goodsName = map.get("goodsName");
        String gsName = map.get("gsName");
        String authDate = ToolUtils.getNowDateFormat("yyyy年MM月dd日");
        Double rentAfterCoupon = order.getRentAfterCoupon();
        LocalDateTime planBegin = order.getPlanBegin();
        int year1 = planBegin.getYear();
        int month1 = planBegin.getMonthValue();
        int day1 = planBegin.getDayOfMonth();
        LocalDateTime planEnd = order.getPlanEnd();
        int year2 = planEnd.getYear();
        int month2 = planEnd.getMonthValue();
        int day2 = planEnd.getDayOfMonth();
        int orderDay = order.getDay();
        double onePrice = ToolUtils.divideAndCeil(rentAfterCoupon, order.getNum());
        double totalPrice = ToolUtils.add(rentAfterCoupon, order.getShippingFee());
        Double buyPriceAfterStore = order.getBuyPriceAfterStore();
        double buyPrice = buyPriceAfterStore;
        double oneBuyPrice = ToolUtils.divideAndCeil(buyPriceAfterStore, order.getNum());
        String platformName = ConfigKey.platformName;//平台法人代表
        String jfQsq = map.get("storeSeal");
        String pdfpath = ConfigKey.pdfpath;
        if (ToolUtils.isEmpty(jfQsq)) {
            String imgPath = PersonalSealUtil.seal(legalPersonName, 1, 1, pdfpath);
            jfQsq = OssService.uploadBtFile(imgPath, "seal/store/" + legalPersonName + ".png");
            tStoreDetailMapper.updateStoreSeal(order.getStoreid(), jfQsq);
        }
        String yfQsq = map.get("customerseal");
        if (ToolUtils.isEmpty(yfQsq)) {
            String imgPath = PersonalSealUtil.seal(username, 1, 1, pdfpath);
            yfQsq = OssService.uploadBtFile(imgPath, "seal/customer/" + username + ".png");
            tCustomerMapper.updateCustomerSeal(order.getCustomerid(), yfQsq);
        }
        String bfQsq = ConfigKey.sealId;

        List<Map<String, Object>> tableData = new ArrayList<>();
        Map<String, Object> data0 = new HashMap<>();
        Map<String, Object> row0 = new HashMap<>();
        row0.put("column1", "租期");
        row0.put("column2", "租金");
        row0.put("column3", "租金付款日");
        data0.put("row", row0);
        tableData.add(data0);
        for (int i = 0; i < bills.size(); i++) {
            TOrderBill bill = bills.get(i);
            Map<String, Object> data = new HashMap<>();
            data.put("insertRow", true);
            Map<String, Object> row = new HashMap<>();
            row.put("column1", "第" + (i + 1) + "期");
            row.put("column2", bill.getPayAmount());
            row.put("column3", bill.getPlanDate());
            data.put("row", row);
            tableData.add(data);
        }
        String billJson = JSON.toJSONString(tableData);
        /*StringBuilder sb = new StringBuilder();
        for (int i = 0; i < bills.size(); i++) {
            TOrderBill bill = bills.get(i);
            String c1 = "第" + (i + 1) + "期";
            String c2 = "" + bill.getPayAmount();
            String c3 = bill.getPlanDate();
            int len = 15;
            sb.append(ToolUtils.padString(c1, len));
            sb.append(ToolUtils.padString(c2, len));
            sb.append(ToolUtils.padString(c3, len));
            if (i != bills.size() - 1) {
                sb.append("\n");
            }
        }
        String billJson = sb.toString();*/

        String docTemplateId = ConfigKey.docTemplateId;
        String docTemplate = getDocTemplate(docTemplateId);
        Map<String, Object> result = JSON.parseObject(docTemplate, Map.class);
        List<Component> components = new ArrayList<>();
        if (result != null && result.get("code").equals(0)) {
            Map<String, Object> data = (Map<String, Object>) result.get("data");
            List<Map> componentList = JSON.parseArray(ToolUtils.objToString(data.get("components")), Map.class);
            for (Map<String, Object> component : componentList) {
                Component c = new Component();
                c.setComponentId(ToolUtils.objToString(component.get("componentId")));
                c.setComponentKey(ToolUtils.objToString(component.get("componentKey")));
                String componentName = ToolUtils.objToString(component.get("componentName"));
                Object componentValue = switch (componentName) {
                    case "协议编号", "订单编号" -> orderCode;
                    case "签约价" -> rentAfterCoupon;
                    case "起始年" -> year1;
                    case "起始月" -> month1;
                    case "起始日" -> day1;
                    case "截至年" -> year2;
                    case "截止月" -> month2;
                    case "截止日" -> day2;
                    case "天" -> orderDay;
                    case "单件租赁物订单总费用" -> onePrice;
                    case "单件租赁物留购款" -> oneBuyPrice;
                    case "所有租赁物订单总费用" -> totalPrice;
                    case "所有租赁物留购款" -> buyPrice;
                    case "甲方签署区" -> jfQsq;
                    case "乙方签署区" -> yfQsq;
                    case "丙方签署区", "签署区" -> bfQsq;
                    case "甲方商户名称", "甲方公司名称", "甲方企业名称" -> storename;
                    case "甲方地址" -> storeaddress;
                    case "乙方姓名", "乙方公司名称", "乙方代表姓名", "乙方企业名称", "乙方代表名字" -> username;
                    case "乙方地址" -> userAddress;
                    case "乙方身份证号" -> idNumber;
                    case "乙方电话" -> userphone;
                    case "乙方邮箱" -> "";
                    case "商品名称" -> goodsName;
                    case "商品套餐" -> gsName;
                    case "甲方代表姓名", "甲方代表名字" -> legalPersonName;
                    case "日期" -> authDate;
                    case "丙方代表姓名", "丙方代表名字" -> platformName;
                    case "账单详情" -> billJson;
                    case "第一期租金" -> ToolUtils.objToString(bills.get(0).getPayAmount());
                    case "第一期租金付款日" -> bills.get(0).getPlanDate();
                    default -> null;
                };
                c.setComponentValue(componentValue);
                components.add(c);
            }
        } else {
            throw new ServiceException("获取模板失败:" + result.get("message"));
        }
        TemplateDTO dto = new TemplateDTO();
        dto.setFileName(orderCode);
        dto.setDocTemplateId(docTemplateId);
        dto.setComponents(components);
        String template = createByTemplate(dto);
        try {
            JSONObject jsonObject = JSON.parseObject(template, JSONObject.class);
            if (!jsonObject.getString("code").equals("0")) {
                throw new ServiceException(jsonObject.getString("message"));
            }
            String fileId = jsonObject.getJSONObject("data").getString("fileId");
            contract.setFileId(fileId);

            JSONObject data = getFileDownload(fileId);
            if (data.get("fileStatus").equals(5)) {//文件已转换
                //发起签署流程
                JSONObject flowObject = signFlowCreate(fileId, data.getString("fileName"), "租赁合同-" + orderCode);
                if (flowObject.getString("code").equals("0")) {
                    String signFlowId = flowObject.getJSONObject("data").getString("signFlowId");
                    contract.setSignFlowId(signFlowId);

                    signFlowDetail(signFlowId);

                    String path = signFlowFileDownload(signFlowId);
                    contract.setPath(path);
                } else {
                    throw new ServiceException(flowObject.getString("message"));
                }
            }
        } catch (Exception e) {
            EqbService.log.error("生成合同失败:{}", e.getMessage());
        }
        return contract;
    }

    //基于文件发起签署
    public JSONObject signFlowCreate(String fileId, String fileName, String signFlowTitle) {
        //创建签署流程
        JSONObject jsonObject = new JSONObject();

        // 添加 docs
        JSONArray docsArray = new JSONArray();
        JSONObject docObject = new JSONObject();
        docObject.put("fileId", fileId);
        docObject.put("fileName", fileName);
        docsArray.add(docObject);
        jsonObject.put("docs", docsArray);

        // 添加 signFlowConfig
        JSONObject signFlowConfigObject = new JSONObject();
        signFlowConfigObject.put("signFlowTitle", signFlowTitle);
        signFlowConfigObject.put("autoFinish", true);
        jsonObject.put("signFlowConfig", signFlowConfigObject);

        // 添加 signers
        JSONArray signersArray = new JSONArray();
        JSONObject signerObject = new JSONObject();
        signerObject.put("signerType", 1);

        // 添加 orgSignerInfo
        JSONObject orgSignerInfoObject = new JSONObject();
        orgSignerInfoObject.put("orgId", ConfigKey.orgId);
        signerObject.put("orgSignerInfo", orgSignerInfoObject);

        // 添加 signFields
        JSONArray signFieldsArray = new JSONArray();
        List<JSONObject> positions = JSON.parseArray(ConfigKey.signFieldPosition, JSONObject.class);
        for (JSONObject position : positions) {
            JSONObject signFieldObject = new JSONObject();
            signFieldObject.put("fileId", fileId);

            // 添加 normalSignFieldConfig
            JSONObject normalSignFieldConfigObject = new JSONObject();
            normalSignFieldConfigObject.put("freeMode", false);
            normalSignFieldConfigObject.put("autoSign", true);
            normalSignFieldConfigObject.put("signFieldStyle", 1);

            // 添加 signFieldPosition
            normalSignFieldConfigObject.put("signFieldPosition", position);

            signFieldObject.put("normalSignFieldConfig", normalSignFieldConfigObject);
            signFieldsArray.add(signFieldObject);
        }
        signerObject.put("signFields", signFieldsArray);

        // 将签署者添加到签署者数组
        signersArray.add(signerObject);
        jsonObject.put("signers", signersArray);
        String result = EqbService.testPost(jsonObject, "/v3/sign-flow/create-by-file");
        return JSON.parseObject(result, JSONObject.class);
    }

    //查询签署流程
    public JSONObject signFlowDetail(String signFlowId) {
        try {
            Thread.sleep(1000);
            String result = EqbService.testGet("/v3/sign-flow/" + signFlowId + "/detail");
            JSONObject object = JSON.parseObject(result, JSONObject.class);
            int signFlowStatus = object.getJSONObject("data").getIntValue("signFlowStatus");
            if (signFlowStatus == 0 || signFlowStatus == 1) {//草稿或签署中
                Thread.sleep(500);
                return signFlowDetail(signFlowId);
            } else if (signFlowStatus == 2) {//完成
                return object;
            } else {
                String message;
                if (signFlowStatus == 3) {
                    message = "撤销";
                } else if (signFlowStatus == 5) {
                    message = "过期";
                } else if (signFlowStatus == 7) {
                    message = "拒签";
                } else {
                    message = "未知状态";
                }
                throw new ServiceException(message);
            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    //下载已签署文件及附属材料
    public String signFlowFileDownload(String signFlowId) {
        String result = EqbService.testGet("/v3/sign-flow/" + signFlowId + "/file-download-url");
        JSONObject object = JSON.parseObject(result, JSONObject.class);
        StringBuilder sb = new StringBuilder();
        if (object.get("code").equals(0)) {
            JSONArray files = object.getJSONObject("data").getJSONArray("files");
            for (int i = 0; i < files.size(); i++) {
                JSONObject file = files.getJSONObject(i);
                String fileName = file.getString("fileName");
                String downloadUrl = file.getString("downloadUrl");
                String path = OssService.uploadFile3(downloadUrl, "admin_pdf/order/" + ToolUtils.getNowDateFormat("yyyy-MM-dd") + "/" + fileName);
                if (i != 0) {
                    sb.append(",");
                }
                sb.append(path);
            }
        }
        return sb.toString();
    }

    /**
     * @Description 获取文件下载地址
     * @Date 2024/10/15 13:04
     * @Author YWX
     * @Param [fileId]
     * @Return com.alibaba.fastjson.JSONObject
     **/
    public JSONObject getFileDownload(String fileId) {
        try {
            Thread.sleep(1000);
            String uploadUrl = EqbService.testGet("/v3/files/" + fileId);//查询文件上传状态
            JSONObject jsonObject = JSON.parseObject(uploadUrl, JSONObject.class);
            JSONObject data = jsonObject.getJSONObject("data");
            String fileDownloadUrl = data.getString("fileDownloadUrl");
            if (ToolUtils.isEmpty(fileDownloadUrl)) {
                Thread.sleep(500);
                return getFileDownload(fileId);
            }
            return data;
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * @Description 生成租赁合同-基于本地生成的pdf文件
     * @Date 2024/10/19 9:35
     * @Author YWX
     * @Param [filePath, orderCode]
     * @Return cn.psvmc.cxzapi.model.order.TOrderContract
     **/
    public void getContractUrl2(String filePath, String orderCode, Integer orderId, Integer type, RequestAttributes requestAttributes) {
        // 恢复请求上下文
        RequestContextHolder.setRequestAttributes(requestAttributes, true);
        //获取文件上传地址
        String contentMD5 = EqbService.getStringContentMD5(filePath);
        String contentType = "application/pdf";
        JSONObject object = new JSONObject();
        object.put("contentMd5", contentMD5);
        object.put("contentType", contentType);
        File file = new File(filePath);
        String fileName = file.getName();
        object.put("fileName", fileName);
        object.put("fileSize", file.length());
        String result = EqbService.testPost(object, "/v3/files/file-upload-url");
        JSONObject obj = EqbService.strToJsonObject(result);
        if (!obj.get("code").equals(0)) {
            throw new ServiceException(obj.getString("msg"));
        }

        JSONObject data = obj.getJSONObject("data");
        TOrderContract contract = new TOrderContract();
        String fileId = data.getString("fileId");
        contract.setFileId(fileId);
        //上传文件流
        boolean rtn = EqbService.uploadFile(filePath, data.getString("fileUploadUrl"));
        data = getFileDownload(fileId);
        Object fileStatus = data.get("fileStatus");
        if (fileStatus.equals(2)) {//文件已上传
            //发起签署流程
            JSONObject flowObject = signFlowCreate(fileId, data.getString("fileName"), "租赁合同-" + orderCode);
            if (flowObject.getString("code").equals("0")) {
                String signFlowId = flowObject.getJSONObject("data").getString("signFlowId");
                contract.setSignFlowId(signFlowId);

                signFlowDetail(signFlowId);

                String path = signFlowFileDownload(signFlowId);
                contract.setPath(path);
            } else {
                throw new ServiceException(flowObject.getString("message"));
            }
        }
        FileUtils.deleteFile(filePath);//删除本地文件

        contract.setOrderId(orderId);
        contract.setType(type);
        orderContractMapper.insert(contract);
    }
}
